package com.fowo.api.sys.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.model.RException;
import com.fowo.api.config.AppProperties;
import com.fowo.api.sys.annotation.DataActivity;
import com.fowo.api.sys.entity.SysTask;
import com.fowo.api.sys.entity.enums.SysDataActivityAction;
import com.fowo.api.sys.entity.enums.SysTaskType;
import com.fowo.api.sys.mapper.SysTaskMapper;
import com.fowo.api.sys.model.SysTaskItemVo;
import com.fowo.api.sys.model.SysTaskSearch;
import com.fowo.api.sys.model.SysTaskVo;
import com.fowo.api.sys.service.SysTaskExecutionService;
import com.fowo.api.sys.service.SysTaskService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class SysTaskServiceImpl extends ServiceImpl<SysTaskMapper, SysTask> implements SysTaskService {

    @Resource
    private SysTaskExecutionService sysTaskExecutionService;

    @Resource
    private AppProperties appProperties;

    @Override
    public Page<SysTaskItemVo> pageSearch(SysTaskSearch search) {
        return baseMapper.pageSearch(search.toPage(), search);
    }

    @Override
    public List<SysTaskItemVo> search(SysTaskSearch search) {
        return baseMapper.search(search);
    }

    @Override
    @DataActivity(action = SysDataActivityAction.Add)
    public boolean create(SysTaskVo sysTaskVo) {
        SysTask sysTask = new SysTask();
        BeanUtils.copyProperties(sysTaskVo, sysTask);
        sysTask.doCreate();

        checkSysTask(sysTask);

        if(save(sysTask)) {
            sysTaskExecutionService.touch(sysTask.getId());
            return true;
        }
        return false;
    }

    @Override
    @DataActivity(action = SysDataActivityAction.Modified)
    public boolean update(SysTaskVo sysTaskVo) {
        SysTask oldSysTask = getById(sysTaskVo.getId());
        if (oldSysTask == null) {
            throw new RException("未找到此任务");
        }
        SysTask updateSysTask = new SysTask();
        BeanUtils.copyProperties(sysTaskVo, updateSysTask);
        updateSysTask.doUpdate();

        checkSysTask(updateSysTask);

        if(updateById(updateSysTask)) {
            sysTaskExecutionService.touch(sysTaskVo.getId());
            return true;
        }
        return false;
    }

    @Override
    public void terminate(Long id) throws Exception {
        SysTask sysTask = baseMapper.selectById(id);
        if (sysTask == null) {
            // 没有任务忽略终止任务
            return;
        }
        if (sysTask.getStatus() == 0) {
            return;
        }
        if (Objects.equals(appProperties.getWorkerId(), sysTask.getPlanWorkerId())) {
            // 本机任务节点
            sysTaskExecutionService.stop(appProperties.getWorkerId(), id);
        }
        else {
            throw new RException("当前不支持多服务模式终止任务!");
        }
    }

    private void checkSysTask(SysTask sysTask) {
        codeUniqueCheck(sysTask);
        innerClassMethodCheck(sysTask);
        checkPlanState(sysTask);
    }

    private void checkPlanState(SysTask sysTask) {
        if (sysTask.getPlan() != null && sysTask.getPlan()) {
            if (sysTask.getData() == null) {
                // 需要 data 中的数据
                SysTask oldSysTask = getById(sysTask.getId());
                if ("none".equals(oldSysTask.getData().getLoopType())) {
                    throw new RException("此任务没有周期配置，不能进行计划");
                }
            }
        }
    }

    private void innerClassMethodCheck(SysTask sysTask) {
        if (sysTask != null && sysTask.getType() == SysTaskType.Inner && sysTask.getData() != null) {
            try {
                Class runClass = Class.forName(sysTask.getData().getClassName());
                final List<Method> matchMethods = Arrays.stream(runClass.getMethods()).filter(m -> m.getName().equals(sysTask.getData().getMethod())).collect(Collectors.toList());
                if (matchMethods.size() == 0) {
                    throw new RException("方法未找到");
                }
                if (matchMethods.size() > 1) {
                    throw new RException("方法有多个重名重载");
                }
                final Method method = matchMethods.get(0);
                if (method.getParameterCount() > 1) {
                    throw new RException("方法最多只能有一个参数");
                }
            } catch (ClassNotFoundException e) {
                throw new RException("类名错误，未找到此类");
            }
        }
    }

    private void codeUniqueCheck(SysTask sysTask) {
        if (StringUtils.hasText(sysTask.getCode())) {
            if(baseMapper.countByCode(sysTask.getCode(), sysTask.getId()) > 0) {
                throw new RException("任务编码不能重复");
            }
        }
    }

    @Override
    @DataActivity(action = SysDataActivityAction.Del)
    public boolean delete(Long id) {
        SysTask oldSysTask = getById(id);
        if (oldSysTask == null) {
            throw new RException("未找到此任务");
        }
        if (oldSysTask.isProtected()) {
            throw new RException("内置任务不能删除");
        }
        if(removeById(id)) {
            sysTaskExecutionService.touch(id);
            return true;
        }
        return false;
    }

    @Override
    public SysTaskVo getVoById(Long id) {
        SysTask sysTask = getById(id);
        if (sysTask != null) {
            SysTaskVo vo = new SysTaskVo();
            BeanUtils.copyProperties(sysTask, vo);
            return vo;
        }
        return null;
    }
}
